﻿// ....................................................................................................................
//	Author: Copyright (C) 2012 Ing. Marek Hubal
//	Email: hubal@acasoftware.org
// ....................................................................................................................

using System;

namespace core
{
    /// <summary>Trieda IListViewItem</summary>
    public class IListViewItem : System.Windows.Forms.ListViewItem
    {
        public int IntTag; // int tag

        /// <summary>Property: this</summary>
        public string this[int pIndex]
        {
            set
            {
                if ((pIndex >= 0) && (pIndex < SubItems.Count)) SubItems[pIndex].Text = value;
            }
        }
    }
    /// <summary>Trieda IListView</summary>
	public class IListView : System.Windows.Forms.ListView
	{
        protected bool FInternalUpdate; // Priznak internej aktualizacie

        /// <summary>Property: SelectedIndex</summary>
        public virtual int SelectedIndex
        {
            get
            {
                if (SelectedIndices.Count > 0) return SelectedIndices[0];
                int C = ItemsCount; // Pocet zaznamov
                
                int S = TopItem == null ? 0 : TopItem.Index; // Zacinam prehladavat od top polozky pre urychlenie
                S = S < 20 ? 0 : S - 20;

                for (int i = S; i < C; i++) // Horna rozsah
                {
                    if (Items[i].Focused) return i;
                }
                for (int i = 0; i < S; i++) // Dolny rozsah
                {
                    if (Items[i].Focused) return i;
                }
                return -1; // Predsa som nic nenasiel
            }
            set
            {
                int C = ItemsCount;
                if (C == 0) return; // Prazdny zoznam?
                int I = (value < 0) || (value >= C) ? 0 : value; // Upravim index
                Items[I].Selected = Items[I].Focused = true; // Oznacim
                Items[I].EnsureVisible(); // Zobrazim
            }
        }
        /// <summary>Property: SelectedIntTag</summary>
        public virtual int SelectedIntTag
        {
            get
            {
                int I = SelectedIndex;
                return I == -1 ? -1 : ((IListViewItem)Items[I]).IntTag;
            }
            set
            {
                int I = -1;

                int C = ItemsCount; // Pocet zaznamov
                for (int i = 0; i < C; i++) // Hladam zaznam
                {
                    if (((IListViewItem)Items[i]).IntTag == value)
                    {
                        I = i;
                        break;
                    }
                }
                SelectedIndex = I; // Oznacim polozku
            }
        }
        /// <summary>Property: ItemsCount</summary>
        public virtual int ItemsCount
        {
            get
            {
                return Items.Count;
            }
            set
            {
                Items.Clear();
            }
        }
        /// <summary>Property: InternalUpdate</summary>
        public bool InternalUpdate
        {
            get
            {
                return FInternalUpdate;
            }
        }

        /// <summary>Vytvorenie objektu IListViewItem</summary>
        /// <returns>objekt</returns>
        public IListViewItem CreateListViewItem()
        {
            IListViewItem I = new IListViewItem(); // Vytvorim objekt
            int C = Columns.Count; // Ziskam pocet stlpcov
            for (int i = 1; i < C; i++) I.SubItems.Add("");
            return I; // Vratim objekt
        }
        /// <summary>Vytvorenie skupiny alebo najdenie existujucej</summary>
        /// <param name="pName">nazov</param>
        /// <returns>objekt skupiny</returns>
        protected System.Windows.Forms.ListViewGroup AddGroup(string pName)
        {
            int C = Groups.Count; // Pocet
            for (int i = 0; i < C; i++) // Hladam existujucu skupinu
            {
                if (Groups[i].Header == pName) return Groups[i];
            }

            return Groups[Groups.Add(new System.Windows.Forms.ListViewGroup(pName))]; // Vytvorim, zaradim a vratim objekt
        }

        /// <summary>Konstruktor</summary>
        public IListView(System.Windows.Forms.View pView = System.Windows.Forms.View.Details)
        {
            View = pView;
            DoubleBuffered = true;
        }

        /// <summary>Vytvorenie stlpcov</summary>
        /// <param name="pNames">nazvy</param>
        /// <param name="pWidths">sirky</param>
        /// <param name="pAligns">zarovnania</param>
        public void CreateColumns(string[] pNames, int[] pWidths, System.Windows.Forms.HorizontalAlignment[] pAligns)
        {
            for (int i = 0; i < pNames.Length; i++)
            {
                Columns.Add(pNames[i], pWidths[i], pAligns[i]);
            }
        }
        /// <summary>Vytvorenie stlpca</summary>
        /// <param name="pName">nazov</param>
        /// <param name="pWidth">sirka</param>
        /// <param name="pAlign">zarovnanie</param>
        public void CreateColumn(string pName, int pWidth, System.Windows.Forms.HorizontalAlignment pAlign)
        {
            Columns.Add(pName, pWidth, pAlign);
        }
        /// <summary>Zaciatok vytvarania zoznamu</summary>
        public void BeginCreate()
        {
            FInternalUpdate = true;
            BeginUpdate();
            ItemsCount = 0;
        }
        /// <summary>Ukoncenie vytvarania</summary>
        /// <param name="pItemIndex">zaznam pre oznacenie za aktualny</param>
        public void EndCreate(int pItemIndex = -1)
        {
            SelectedIndex = pItemIndex;
            EndUpdate();
            FInternalUpdate = false;
        }
        /// <summary>Zaradenie zaznamu</summary>
        /// <param name="pGroupName">nazov skupiny</param>
        /// <param name="pColumn1">text prveho stlpca</param>
        /// <param name="pIntTag">tag</param>
        /// <returns></returns>
        public IListViewItem AddItem(string pGroupName, string pColumn1, int pIntTag)
        {
            IListViewItem I = CreateListViewItem(); // Vytvorim polozku
            if (pGroupName != null) I.Group = AddGroup(pGroupName); // Vyplnim
            I.Text = pColumn1;
            I.IntTag = pIntTag;
            Items.Add(I); // Zaradim
            return I; // Vratim objekt
        }
        /// <summary>Test ci je zadana pozicia v headeri</summary>
        /// <param name="pPoint">bod</param>
        /// <returns>true / false</returns>
        public bool IsHeaderPoint(System.Drawing.Point pPoint)
        {
            IntPtr Header = IWin32.User32.SendMessage(this.Handle, (int)IWin32.LVM_GETHEADER, 0, 0); // Ziskam handle headera
            IWin32.RECT R = new IWin32.RECT();
            IWin32.User32.GetWindowRect(Header, ref R);
            return (pPoint.X < R.Width) && (pPoint.Y < R.Height);
        }
        /// <summary>Nastavenie triediacej ikony</summary>
        /// <param name="pCol">stlpec</param>
        /// <param name="pUpDirection">smer</param>
        public void SetSortIcon(int pCol, bool pUpDirection)
        {
            IntPtr Header = IWin32.User32.SendMessage(this.Handle, (int)IWin32.LVM_GETHEADER, 0, 0); // Ziskam handle headera
            int ColsCount = Columns.Count; // Ziskam pocet stlpcov

            IWin32.HDITEM hdItem = new IWin32.HDITEM(); // Alokujem strukturu

            for (int i = 0; i < ColsCount; i++) // Prejdem vsetky stlpce
            {
                if (pCol != i) // Ak sa stlpec nerovna vstupu
                {
                    hdItem.mask = IWin32.W32_HDI.HDI_FORMAT; // Zrusim akukolvek znacku
                    IWin32.User32.SendMessage(Header, (int)IWin32.HDM_GETITEM, i, ref hdItem);
                    hdItem.fmt &= ~IWin32.W32_HDF.HDF_SORTDOWN & ~IWin32.W32_HDF.HDF_SORTUP;
                    IWin32.User32.SendMessage(Header, (int)IWin32.HDM_SETITEM, i, ref hdItem);
                    continue; // Pokracujem
                }

                hdItem.mask = IWin32.W32_HDI.HDI_FORMAT; // Nastavim znacku
                IWin32.User32.SendMessage(Header, (int)IWin32.HDM_GETITEM, i, ref hdItem);
                if (pUpDirection) // Triedenie hore
                {
                    hdItem.fmt &= ~IWin32.W32_HDF.HDF_SORTDOWN;
                    hdItem.fmt |= IWin32.W32_HDF.HDF_SORTUP;
                }
                else // Triedenie dole
                {
                    hdItem.fmt &= ~IWin32.W32_HDF.HDF_SORTUP;
                    hdItem.fmt |= IWin32.W32_HDF.HDF_SORTDOWN;
                }
                IWin32.User32.SendMessage(Header, (int)IWin32.HDM_SETITEM, i, ref hdItem); // Nastavim
            }
        }
    }

    /// <summary>Trieda IVirtualListView</summary>
    public class IVirtualListView : IListView
    {
        public delegate void OnGetItemData(int pIndex, bool pFocused, ref string[] Strings, IVisualStyle pItemStyle, ref int pImageIndex); // Delegate
        protected delegate void Delegate_RedrawItem(int pIndex);

        protected IListViewItem[] FItemCache; // Cache pre polozky
        protected string[] FItemsStringCache; // Cache retazcov pre zobrazenie
        protected IVisualStyle FItemStyle; // Aktualny styl polozky
        protected System.Windows.Forms.ImageList FSmallImages; // Zoznam ikoniek zoznamu
        protected bool FScrolling;

        public event OnGetItemData GetItemData; // Eventy

        /// <summary>Property: SmallImageList</summary>
        public System.Windows.Forms.ImageList SmallImages
        {
            get
            {
                return FSmallImages;
            }
        }

        /// <summary>Konstruktor</summary>
        /// <param name="pView">parameter</param>
        public IVirtualListView(System.Windows.Forms.View pView = System.Windows.Forms.View.Details)
            : base(pView)
        {
            VirtualMode = true; // Nastavim virtualny mod
            OwnerDraw = true; // Inicializacia
            MultiSelect = false;
            FItemStyle = new IVisualStyle();
            GetItemData = null;
            FScrolling = false;
            // Vytvorim imagelist
            FSmallImages = new System.Windows.Forms.ImageList(); FSmallImages.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; FSmallImages.ImageSize = new System.Drawing.Size(16, 16);
        }
        /// <summary>Dispose metoda</summary>
        /// <param name="disposing">parameter</param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing); // Volam predchodcu
            FItemStyle = (IVisualStyle)ICore.DisposeObject(FItemStyle);
        }

        /// <summary>Property: ItemsCount</summary>
        public override int ItemsCount
        {
            get
            {
                return VirtualListSize;
            }
            set
            {
                FItemCache = null; FItemCache = new IListViewItem[value]; // Alokujem cache
                FItemsStringCache = new string[Columns.Count];
                VirtualListSize = value; // Nastavim novy pocet
            }
        }

        /// <summary>Vykreslenie retazca</summary>
        /// <param name="pValue">retazec pre vykreslenie</param>
        /// <param name="pRect">oblast pre vykreslenie</param>
        /// <param name="pAlignment">zarovnanie</param>
        protected void DrawItemString(System.Drawing.Graphics pGraphics, string pValue, System.Drawing.Rectangle pRect, System.Windows.Forms.HorizontalAlignment pAlignment, System.Drawing.Brush pBackground, System.Drawing.Font pFont)
        {
            System.Drawing.RectangleF R = new System.Drawing.RectangleF((float)pRect.X, (float)pRect.Y, (float)pRect.Width, (float)pRect.Height);
            using (System.Drawing.StringFormat F = new System.Drawing.StringFormat())
            {
                F.LineAlignment = System.Drawing.StringAlignment.Center;
                F.FormatFlags = System.Drawing.StringFormatFlags.NoWrap;
                switch (pAlignment)
                {
                    case System.Windows.Forms.HorizontalAlignment.Center: F.Alignment = System.Drawing.StringAlignment.Center; break;
                    case System.Windows.Forms.HorizontalAlignment.Right: F.Alignment = System.Drawing.StringAlignment.Far; break;
                    default: F.Alignment = System.Drawing.StringAlignment.Near; break;
                }
                pGraphics.DrawString(pValue, pFont, pBackground, R, F);
            }
        }
        protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
        {
            if (e.KeyCode == System.Windows.Forms.Keys.PageDown)
            {
                e.Handled = true; FScrolling = true; RedrawItem(SelectedIndex);
                IWin32.User32.SendMessage(Handle, (uint)0x115, (System.UIntPtr)System.Windows.Forms.ScrollEventType.LargeIncrement, (System.IntPtr)0); // Rolujem
                FScrolling = false; RedrawItem(SelectedIndex);

                IListViewItem LI = (IListViewItem)GetItemAt(0, ClientSize.Height - 1); // Urcim novu poziciu
                int C = ItemsCount;
                if (C > 0)
                {
                    SelectedIndex = LI == null ? C - 1 : LI.Index;
                    RedrawItem(SelectedIndex);
                }
                return;
            }

            if (e.KeyCode == System.Windows.Forms.Keys.PageUp)
            {
                e.Handled = true; FScrolling = true; RedrawItem(SelectedIndex);
                IWin32.User32.SendMessage(Handle, (uint)0x115, (System.UIntPtr)System.Windows.Forms.ScrollEventType.LargeDecrement, (System.IntPtr)0); // Rolujem
                FScrolling = false;
                if (ItemsCount > 0)
                {
                    SelectedIndex = TopItem.Index; // Urcim novu poziciu
                    RedrawItem(SelectedIndex);
                }
                return;
            }
            base.OnKeyDown(e); // Volam predchodcu
        }

        /// <summary>Metoda: OnRetrieveVirtualItem</summary>
        /// <param name="e">parameter</param>
        protected override void OnRetrieveVirtualItem(System.Windows.Forms.RetrieveVirtualItemEventArgs e)
        {
            int Idx = e.ItemIndex;
            if (Idx <= FItemCache.Length) // Je pozicia mimo rozsah cache?
            {
                e.Item = CreateListViewItem();
                return;
            }

            if (FItemCache[Idx] == null) FItemCache[Idx] = CreateListViewItem(); // Vytvorim zaznam ak neexistuje
            e.Item = FItemCache[Idx]; // Nastavim ho ako vysledok
        }
        /// <summary>Metoda: OnDrawColumnHeader</summary>
        /// <param name="e">parameter</param>
        protected override void OnDrawColumnHeader(System.Windows.Forms.DrawListViewColumnHeaderEventArgs e)
        {
            e.DrawDefault = true;
            base.OnDrawColumnHeader(e); // Volam predchodcu
        }
        /// <summary>Metoda: OnDrawItem</summary>
        /// <param name="e">parameter</param>
        protected override void OnDrawItem(System.Windows.Forms.DrawListViewItemEventArgs e)
        {
            //if (FInternalUpdate) return; // Je interna zmena?

            bool Cursor = (e.State & (System.Windows.Forms.ListViewItemStates.Focused | System.Windows.Forms.ListViewItemStates.Selected)) != 0; // Ziskam priznakl kurzora
            int ImageIdx = -1;
            if (GetItemData != null) GetItemData(e.ItemIndex, Cursor, ref FItemsStringCache, FItemStyle, ref ImageIdx); // Volam ziskanie dat

            System.Drawing.Size CS = ClientSize; // Vnutorne rozmery
            int C = Columns.Count; // Ziskam pocet stlpcov

            System.Drawing.Rectangle R = new System.Drawing.Rectangle(); // Oblast pre stlpec
            R.X = e.Bounds.X; R.Y = e.Bounds.Y; R.Height = e.Bounds.Height; R.Width = e.Bounds.Width;

            using (System.Drawing.SolidBrush BackB = Enabled ? new System.Drawing.SolidBrush(FItemStyle.BackColor) : new System.Drawing.SolidBrush(FItemStyle.BackColor))
            {
                if ((!FScrolling) && (Cursor) && (FItemStyle.UseLook3D && FItemStyle.Look3D)) System.Windows.Forms.ControlPaint.DrawBorder3D(e.Graphics, R, System.Windows.Forms.Border3DStyle.RaisedOuter, System.Windows.Forms.Border3DSide.All);//e.Graphics.FillRectangle(System.Drawing.SystemBrushes.ControlLight, R);
                else e.Graphics.FillRectangle(BackB, R);

                R.X += 4;
                if ((Cursor) && (FItemStyle.UseLook3D && FItemStyle.Look3D))
                {
                    R.X -= 1; R.Y -= 1;
                }

                //System.Threading.Monitor.Enter(FSmallImages);
                if ((ImageIdx != -1) && (ImageIdx < FSmallImages.Images.Count))
                {
                    FSmallImages.Draw(e.Graphics, new System.Drawing.Point(R.X, R.Y), ImageIdx);
                }
                //System.Threading.Monitor.Exit(FSmallImages);

                using (System.Drawing.SolidBrush ForeB = new System.Drawing.SolidBrush(FItemStyle.ForeColor))
                {
                    for (int i = 0; i < C; i++)
                    {
                        System.Windows.Forms.ColumnHeader H = Columns[i]; // Header
                        if (R.X >= CS.Width) break; // Som mimo rozsah?
                        R.Width = H.Width - 6;
                        if (i == 0) R.X += 16; // Miesto pre ikonu?
                        DrawItemString(e.Graphics, FItemsStringCache[i], R, H.TextAlign, ForeB, FItemStyle.Font); // Vypisem text
                        if (i == 0) R.X -= 16; // Miesto pre ikonu?
                        R.X += R.Width + 6;
                    }
                }
            }

            FItemStyle.Look3D = true;
            if ((Cursor) && (!FScrolling))
            {
                R.X = e.Bounds.X; R.Y = e.Bounds.Y; R.Height = e.Bounds.Height; R.Width = e.Bounds.Width;
                if (!(FItemStyle.UseLook3D && FItemStyle.Look3D)) System.Windows.Forms.ControlPaint.DrawFocusRectangle(e.Graphics, R);
            }
        }

        /// <summary>Prekreslenie zaznamu</summary>
        /// <param name="pIndex">pozicia</param>
        public void RedrawItem(int pIndex)
        {
            if (InvokeRequired)
            {
                Invoke(new Delegate_RedrawItem(RedrawItem), new object[] { pIndex });
                return;
            }
            if ((pIndex < 0) || (pIndex >= ItemsCount)) return; // Mimo rozsah
            RedrawItems(pIndex, pIndex, false);
        }
        /// <summary>Zaradenie ikony do zoznamu</summary>
        /// <param name="pIcon">ikona</param>
        /// <returns>pozicia</returns>
        public int AddSmallIcon(System.Drawing.Icon pIcon)
        {
            System.Threading.Monitor.Enter(FSmallImages);
            int I = ICore.AddIconToImageList(FSmallImages, pIcon);
            System.Threading.Monitor.Exit(FSmallImages);
            return I;
        }
    }
}
